/*
 * Copyright 2011 myTDev.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.mytdev.resources.injection;

import com.mytdev.resources.R;
import java.awt.Image;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.Icon;
import javax.swing.ImageIcon;

/**
 *
 * @author Yann D'Isanto
 */
public final class ResourcesInjectionUtil {

    public static final String PROPERTY_SEPARATOR = ".";
    
    private static final Logger LOG = Logger.getLogger(ResourcesInjectionUtil.class.getName());

    private ResourcesInjectionUtil() {
    }

    public static Object getResourceValue(String key, Class<?> type, R resources) throws ResourceInjectionException {
        Object value = null;
        try {
            if (Image.class.equals(type)) {
                value = R.getImage(key);
            } else if (Icon.class.equals(type) || ImageIcon.class.equals(type)) {
                value = new ImageIcon(R.getImage(key));
            } else {
                value = resources.getProperties().get(key, type);
            }
        } catch (IllegalArgumentException ex) {
            throw new ResourceInjectionException(ex.getLocalizedMessage(), ex);
        } catch (IOException ex) {
            throw new ResourceInjectionException(ex.getLocalizedMessage(), ex);
        }
        return value;
    }

    public static Object getProperty(Object object, String property) throws ResourceInjectionException, IllegalAccessException, InvocationTargetException {
        int index = property.indexOf(".");
        if (index > -1) {
            Object child = getProperty(object, property.substring(0, index));
            return getProperty(child, property.substring(index + 1));
        }
        Object value = null;
        Class<?> objectClass = object.getClass();
        String getterName = buildAccessPropertyMethod(property, prefix.GET);
        try {
            Method getter = objectClass.getMethod(getterName);
            value = getter.invoke(object);
        } catch (NoSuchMethodException ex1) {
            LOG.log(Level.FINE, "getter not found: {0}", ex1.getLocalizedMessage());
            getterName = buildAccessPropertyMethod(property, prefix.IS);
            try {
                Method getter = objectClass.getMethod(getterName);
                value = getter.invoke(object);
            } catch (NoSuchMethodException ex2) {
                LOG.log(Level.FINE, "getter not found: {0}", ex2.getLocalizedMessage());
                try {
                    Field field = objectClass.getDeclaredField(property);
                    value = getFieldValue(field, object);
                } catch (NoSuchFieldException ex) {
                    throw new ResourceInjectionException("property not found for class " + objectClass.getName() + ": " + property, ex);
                }
            }
        }
        return value;
    }

    public static void setProperty(Object object, String property, String key, R resources) throws ResourceInjectionException, IllegalAccessException, InvocationTargetException {
        int index = property.indexOf(PROPERTY_SEPARATOR);
        if (index > -1) {
            object = getProperty(object, property.substring(0, index));
            property = property.substring(index + 1);
        }
        Class<?> objectClass = object.getClass();
        String setterName = buildAccessPropertyMethod(property, prefix.SET);
        boolean setterFound = false;
        for (Method method : objectClass.getMethods()) {
            if (method.getName().equals(setterName)) {
                if (method.getParameterTypes().length == 1) {
                    setterFound = true;
                    method.invoke(object, getResourceValue(key, method.getParameterTypes()[0], resources));
                    break;
                }
            }
        }
        if (!setterFound) {
            LOG.log(Level.FINE, "setter not found: {0}", setterName);
            try {
                Field field = objectClass.getDeclaredField(property);
                setFieldValue(field, object, getResourceValue(key, field.getType(), resources));
            } catch (NoSuchFieldException ex) {
                throw new ResourceInjectionException("property not found for class " + objectClass.getName() + ": " + property, ex);
            }
        }
    }

    public static String buildAccessPropertyMethod(String property, String prefix) {
        StringBuilder sb = new StringBuilder(prefix);
        sb.append(property.substring(0, 1).toUpperCase());
        sb.append(property.substring(1));
        return sb.toString();
    }

    public static Object getFieldValue(Field field, Object object) throws IllegalAccessException {
        boolean accessible = field.isAccessible();
        if (!accessible) {
            field.setAccessible(true);
        }
        Object value = field.get(object);
        if (!accessible) {
            field.setAccessible(accessible);
        }
        return value;
    }

    public static void setFieldValue(Field field, Object object, Object value) throws IllegalAccessException {
        boolean accessible = field.isAccessible();
        if (!accessible) {
            field.setAccessible(true);
        }
        field.set(object, value);
        if (!accessible) {
            field.setAccessible(accessible);
        }
    }

    private interface prefix {

        String GET = "get";
        String IS = "is";
        String SET = "set";
    }
}
